﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Daybreak.Core.SceneGraph;

namespace Daybreak.Core.Rendering
{
    /// <summary>
    /// Geometry describes the visual shape and appearance of an object in the scene. 
    /// </summary>
    public partial class Geometry
    {
        /// <summary>Represents a predefined vertex declaration.</summary>
        public class Apex
        {
            Drawable.Topology.Input[] _inputs;

            VertexElement[] _vertexElements;

            public Apex(int elementCount)
            {
                if (elementCount == 0)
                    return;

                _inputs = new Drawable.Topology.Input[elementCount];
            }

            public static VertexElement[] CreateVertexElements(Drawable.Topology.Input[] inputs)
            {
                if (inputs == null)
                    return null;

                VertexElement[] result = new VertexElement[inputs.Length];

                int offset = 0;

                for (int i = 0; i < inputs.Length; i++)
                {
                    result[i] = inputs[i].CreateVertexElement(0, offset, 0);
                    offset += inputs[i].Size * 4;
                }

                return result;
            }

            public Drawable.Topology.Input[] Inputs
            {
                get { return _inputs; }
                set
                {
                    _inputs = value;
                    _vertexElements = null; // force recreation
                }

            }

            public VertexElement[] VertexElements
            {
                get
                {
                    if (_vertexElements == null)
                        _vertexElements = CreateVertexElements(_inputs);

                    return _vertexElements;
                }
            }
        }

        /// <summary>
        /// Describes a custom vertex format structure that contains position, normal data, and one set of texture coordinates.
        /// </summary>
        public static Apex VertexPosNormTex;
        /// <summary>
        /// Describes a custom vertex format structure that contains position, normal data, texture coordinates and skin info.
        /// </summary>
        public static Apex VertexPosNormTexSkin;

        public string Name;
        public Mesh Mesh;

        static Geometry()
        {
            VertexPosNormTex = new Apex(3);
            VertexPosNormTex.Inputs[0].SetUsage<Vector3>(VertexElementUsage.Position, 0);
            VertexPosNormTex.Inputs[1].SetUsage<Vector3>(VertexElementUsage.Normal, 0);
            VertexPosNormTex.Inputs[2].SetUsage<Vector2>(VertexElementUsage.TextureCoordinate, 0);

            VertexPosNormTexSkin = new Apex(5);
            VertexPosNormTexSkin.Inputs[0].SetUsage<Vector3>(VertexElementUsage.Position, 0);
            VertexPosNormTexSkin.Inputs[1].SetUsage<Vector3>(VertexElementUsage.Normal, 0);
            VertexPosNormTexSkin.Inputs[2].SetUsage<Vector2>(VertexElementUsage.TextureCoordinate, 0);
            VertexPosNormTexSkin.Inputs[3].SetUsage<float>(VertexElementUsage.BlendIndices, 0);
            VertexPosNormTexSkin.Inputs[4].SetUsage<float>(VertexElementUsage.BlendWeight, 0);
        }

        public Geometry()
        {
        }

        public Geometry(Mesh mesh)
        {
            Mesh = mesh;
        }

        public Instance CreateInstance(Scene scene)
        {
            return CreateInstanceBind(scene, null);
        }

        public Instance CreateInstanceBind(Scene scene, Scene.Material.Binding[] bindings)
        {
            Instance result = new Instance(this, bindings);
            result.Parts = new Drawable[Mesh.PartList.Count];

            for (int i = 0; i < Mesh.PartList.Count; i++)
            {
                IMaterial mat = scene.BindMaterial(Mesh.PartList[i].Appearance, bindings);
                result.Parts[i] = new Mesh.Part.Instance(Mesh.PartList[i], mat);
            }

            return result;
        }

        public Instance CreateInstance(Scene scene, IMaterial matOverride)
        {
            Instance result = new Instance(this, null);
            result.Parts = new Drawable[Mesh.PartList.Count];

            for (int i = 0; i < Mesh.PartList.Count; i++)
            {
                result.Parts[i] = new Mesh.Part.Instance(Mesh.PartList[i], matOverride);
            }

            return result;
        }

        public override string ToString()
        {
            return string.Format("<geometry id='{0}'>", Name);
        }

    }
}
